import {
    ref,
    computed,
    onActivated,
    onDeactivated,
    onBeforeUnmount,
} from 'vue';


export type CurrentTime = {
    days: number;
    hours: number | string;
    total: number | string;
    minutes: number | string;
    seconds: number | string;
    milliseconds: number | string;
};

export type UseCountDownOptions = {
    time: number;
    millisecond?: boolean;
    onChange?: (current: CurrentTime) => void;
    onFinish?: () => void;
};

const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;

function parseTime(time: number): CurrentTime {
    const days = Math.floor(time / DAY);
    const hours = (Math.floor((time % DAY) / HOUR) + '')//.padStart(2, '0');
    const minutes = (Math.floor((time % HOUR) / MINUTE) + '')//.padStart(2, '0');
    const seconds = (Math.floor((time % MINUTE) / SECOND) + '')//.padStart(2, '0');
    const milliseconds = (Math.floor(time % SECOND) + '')//.padStart(3, '0');

    return {
        total: time,
        days,
        hours,
        minutes,
        seconds,
        milliseconds,
    };
}

function isSameSecond(time1: number, time2: number): boolean {
    return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
}

export function useCountDown(options: UseCountDownOptions) {
    let rafId: number;
    let endTime: number;
    let counting: boolean;
    let deactivated: boolean;

    const remain = ref(options.time);
    const current = computed(() => parseTime(remain.value));

    const pause = () => {
        counting = false;
        cancelAnimationFrame(rafId);
    };

    const getCurrentRemain = () => Math.max(endTime - Date.now(), 0);

    const setRemain = (value: number) => {
        remain.value = value;
        options.onChange?.(current.value);

        if (value === 0) {
            pause();
            options.onFinish?.();
        }
    };

    const microTick = () => {
        rafId = requestAnimationFrame(() => {
            if (!counting) {
                return
            }
            setRemain(getCurrentRemain());
            remain.value > 0 && microTick();
        });
    };

    const macroTick = () => {
        rafId = requestAnimationFrame(() => {
            if (!counting) {
                return;
            }
            const remainRemain = getCurrentRemain();

            if (!isSameSecond(remainRemain, remain.value) || remainRemain === 0) {
                setRemain(remainRemain);
            }
            remain.value > 0 && macroTick();
        });
    };

    const tick = () => {

        if (options.millisecond) {
            microTick();
        } else {
            macroTick();
        }
    };

    const start = () => {
        if (!counting) {
            endTime = Date.now() + remain.value;
            counting = true;
            tick();
        }
    };

    const reset = (totalTime: number = options.time) => {
        pause();
        remain.value = totalTime;
    };

    onBeforeUnmount(pause);

    onActivated(() => {
        if (deactivated) {
            counting = true;
            deactivated = false;
            tick();
        }
    });

    onDeactivated(() => {
        if (counting) {
            pause();
            deactivated = true;
        }
    });

    return {
        start,
        pause,
        reset,
        current,
    };
}